/* Grammar/parser/grammar_parser.hh
   Written by, Sohail Qayum Malik[sqm@cyberian.pk] */

#include "../../String/String.hh"   
#include "../../parser/parser.hh" 

#ifndef		CC_TOKENIZER_GRAMMAR_PARSER_HH
#define		CC_TOKENIZER_GRAMMAR_PARSER_HH

#define		GRAMMAR_START_OF_LINE_MARKER	':'
#define		GRAMMAR_END_OF_LINE_MARKER	':'
#define		GRAMMAR_START_OF_COMMENT	'#'

#define		END_OF_LINE_MARKER_UNIX		true
#define		END_OF_LINE_MARKER_WINDOWS	false

namespace cc_tokenizer {

template<class _T, typename _E>
class grammar_parser;

template<>
class grammar_parser<cc_tokenizer::String<char>, char> : public cc_tokenizer::parser<cc_tokenizer::String<char>, char> {

   String<char> str;
   
   typename cc_tokenizer::string_character_traits<char>::int_type current_line_number;
   typename cc_tokenizer::string_character_traits<char>::int_type total_number_of_lines;
   typename cc_tokenizer::String<char>::size_type current_line_offset;
   typename cc_tokenizer::String<char>::size_type current_line_size;

   bool end_of_line_marker;
   
   public:
   
      grammar_parser() : str((char *)NULL)  {
     
         current_line_number = cc_tokenizer::string_character_traits<char>::int_type(0);
         current_line_offset = cc_tokenizer::String<char>::size_type(0);
         current_line_size = cc_tokenizer::String<char>::size_type(0);
         total_number_of_lines = cc_tokenizer::String<char>::size_type(0);

	 end_of_line_marker = END_OF_LINE_MARKER_UNIX;
      }
      
      grammar_parser(String<char>& ref) : str(ref) {
      
         current_line_number = cc_tokenizer::string_character_traits<char>::int_type(0);
         current_line_offset = cc_tokenizer::String<char>::size_type(0);
         current_line_size = cc_tokenizer::String<char>::size_type(0);
         
         get_total_number_of_lines();

	 end_of_line_marker = END_OF_LINE_MARKER_UNIX;
      }
      
      cc_tokenizer::String<char> get_current_line(void) {

         cc_tokenizer::String<char>::size_type i;
         cc_tokenizer::String<char> line(str.data() + current_line_offset, current_line_size);

	 
	 
      
         return cc_tokenizer::String<char>(str.data() + current_line_offset, current_line_size);
      } 
      
      cc_tokenizer::String<char> get_line_by_number(cc_tokenizer::string_character_traits<char>::int_type n) {
      
         if ( n > 0 && n != current_line_number && n <= total_number_of_lines ) {
         
            cc_tokenizer::string_character_traits<char>::int_type cln = current_line_number;
            
            if ( n < current_line_number ) {
            
               current_line_number = cc_tokenizer::string_character_traits<char>::int_type(0);
               current_line_offset = cc_tokenizer::String<char>::size_type(0);
               current_line_size = cc_tokenizer::String<char>::size_type(0);               
            }
            
            while ( current_line_number != n ) {
            
               go_to_next_line();
            }              
         }
      
         //return cc_tokenizer::String<char>(str.data() + current_line_offset, current_line_size);
	 return get_current_line();
      }
      
      cc_tokenizer::string_character_traits<char>::int_type get_total_number_of_lines(void) {
      
         if ( str.size() ) {
         
            cc_tokenizer::string_character_traits<char>::int_type cln = current_line_number;
            cc_tokenizer::String<char>::size_type clo = current_line_offset;
            cc_tokenizer::String<char>::size_type cls = current_line_size;
            
            total_number_of_lines = cc_tokenizer::String<char>::size_type(0);
            
            current_line_number = cc_tokenizer::string_character_traits<char>::int_type(0);
            current_line_offset = cc_tokenizer::String<char>::size_type(0);
            current_line_size = cc_tokenizer::String<char>::size_type(0);
            
            while ( go_to_next_line() != cc_tokenizer::string_character_traits<char>::eof() ) {
            
               total_number_of_lines = total_number_of_lines + cc_tokenizer::string_character_traits<char>::int_type(1);            
            }
            
            current_line_number = cln;
            current_line_offset = clo;
            current_line_size = cls;
         }
         
         return total_number_of_lines;
      }     

      /* end-of-line-marker, UNIX '\n' Windows "\r\n" */
      /* A line means a line of Grammar, one such line may have few or many lines of Grammar text, each line of Grammar text will end on end-of-line-marker */
      cc_tokenizer::string_character_traits<char>::int_type go_to_next_line(void) {
      
         cc_tokenizer::string_character_traits<char>::int_type ret = cc_tokenizer::string_character_traits<char>::eof();
         
         typename cc_tokenizer::String<char>::size_type pos_begin = str.find(GRAMMAR_START_OF_LINE_MARKER, current_line_offset + current_line_size);
         
         if ( pos_begin != cc_tokenizer::String<char>::npos ) {
         
            typename cc_tokenizer::String<char>::size_type i = pos_begin;
            
            while ( i && cc_tokenizer::string_character_traits<char>::ne(str[i], '\n') ) {
            
               i = i - cc_tokenizer::String<char>::size_type(1);
            }
            
            if ( i ) {
            
               i = i + cc_tokenizer::String<char>::size_type(1);
            }
            
            current_line_offset = i;
            
            typename cc_tokenizer::String<char>::size_type pos_end = str.find(GRAMMAR_END_OF_LINE_MARKER, pos_begin + cc_tokenizer::String<char>::size_type(1));
            
            if ( pos_end != cc_tokenizer::String<char>::npos ) { /* A line other than the last one is found */

	       typename cc_tokenizer::String<char>::size_type j = pos_end;

	       /* The last line of Grammar line can be followed by many empty lines(just a end-of-line-marker and no content) */
               /* Move from the end to the beginning to the last end of line marker */
	       /* Because elements of array originate at 0 */
	       while ( j > current_line_offset && str[j - 1] != '\n' ) {

	          j = j - cc_tokenizer::String<char>::size_type(1);
	       }

               /* A line can be followed by many empty lines. Remove all such lines */
	       while ( j > current_line_offset && str[j - 1] == '\n' ) {

	          if ( str[j - 2] == '\r' ) { /* May be its a DOS/Windows machine. End of line marker is character pair "\r\n" */ 

		     j = j - cc_tokenizer::String<char>::size_type(2);

		     end_of_line_marker = END_OF_LINE_MARKER_WINDOWS;
		  }
		  else {

		     j = j - cc_tokenizer::String<char>::size_type(1);
		  }
	       }
           
               current_line_size = j - i;
            } 
            else { /* It is the last line of grammar, size it as such that all the end of line markers get removed as well */
           
               typename cc_tokenizer::String<char>::size_type j = str.size();
               
               /* Because elements of array originate at 0  */
	       while ( j > current_line_offset && str[j - 1] == '\n') {

                  /* My be its a DOS/Windows machine, a new line is a character pair "\r\n" */ 
	          if ( str[j - 2] == '\r' ) {

		     j = j - cc_tokenizer::String<char>::size_type(2);

		     end_of_line_marker = END_OF_LINE_MARKER_WINDOWS;
		  } 
		  else { /* No its a UNIX machine, thanks GOD */

		     j = j - cc_tokenizer::String<char>::size_type(1);
		  }
	       }
                 
               current_line_size = j - current_line_offset;
            }
            
            /* We found a new line, remove the comment part of this line(if it has that part as well) */    
            cc_tokenizer::String<char>::size_type cmt_byte_offset = cc_tokenizer::String<char>(str.data() + current_line_offset, current_line_size).find(GRAMMAR_START_OF_COMMENT);
          
            if ( cmt_byte_offset != cc_tokenizer::String<char>::npos ) {
           
               current_line_size = cmt_byte_offset; 
            }
            /* Finished dealing with the comment part of this line */

            /* TODO, */            
            /* Now remove all the new line characters */
            /* Finished dealing with the new line characters */
           
            /* TODO, */ 
            /* Now remove all the consecutive white spaces at the end of the line */            
            /* Finished dealing with all the extra white spaces at the end of the line */
           
            current_line_number = current_line_number + cc_tokenizer::string_character_traits<char>::int_type(1);
            ret = ~ret;              
         }         
         
         return ret;
      }
};
};
#endif  
